package mwm.server.moveresize;

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JComponent;
import javax.swing.SwingUtilities;

public class Resizer extends Delta {
	static final String RESIZING = Resizer.class.getName() + ".RESIZING";
	static final String SHRINK = Resizer.class.getName() + ".SHRINK";
	
	static final String OPERATION = Resizer.class.getName() + ".OPERATION";
	static final String LOCATION = Resizer.class.getName() + ".LOCATION";
	static final String SIZE = Resizer.class.getName() + ".SIZE";
	
	static final int BORDER_THICKNESS = 8;
	
	public Resizer(JComponent component) {
		super(component);
	}
	
	private KeyListener keyListener = new KeyAdapter() {
		boolean grow = true;
		
		public void keyReleased(KeyEvent e) {
			JComponent component = (JComponent) getComponent();

			Point location = null;
			Dimension size = null;
			if (e.getKeyChar() == 'x') {
				if (grow) {
					grow = false;
					component.putClientProperty(SHRINK, Boolean.TRUE);
				} else {
					grow = true;
					component.putClientProperty(SHRINK, null);
				}
				component.repaint();
				e.consume();
				return;
			} else if (e.getKeyChar() == '0') {
				e.consume();
				Dimension shellSize = SwingUtilities.getWindowAncestor(component).getSize();
				size = shellSize;
				location = SwingUtilities.getWindowAncestor(component).getLocation();
				try {
					resize(location, size,  Cursor.MOVE_CURSOR, 0, 0);						
				} finally {
					location = null;
					size = null;
					component.setCursor(MoveResize.getCursor(Cursor.DEFAULT_CURSOR));
				}
			} else if (e.getKeyChar() == '1') {
				e.consume();
				size = component.getSize();
				location = component.getLocation();
				Dimension shellSize = SwingUtilities.getWindowAncestor(component).getSize();
				size.height = shellSize.height-22;
				location.y = 21;
				size.width = shellSize.width;
				location.x = 0;
				try {
					resize(location, size, Cursor.MOVE_CURSOR, 0, 0);						
				} finally {
					location = null;
					size = null;
					component.setCursor(MoveResize.getCursor(Cursor.DEFAULT_CURSOR));
				}
			} else if (e.getKeyChar() == '-') {
				e.consume();
				size = component.getSize();
				location = component.getLocation();
				Dimension shellSize = SwingUtilities.getWindowAncestor(component).getSize();
				size.width = shellSize.width;
				location.x = 0;
				try {
					resize(location, size,  Cursor.MOVE_CURSOR, 0, 0);						
				} finally {
					location = null;
					size = null;
					component.setCursor(MoveResize.getCursor(Cursor.DEFAULT_CURSOR));
				}
			} else if (e.getKeyChar() == '|') {
				e.consume();
				size = component.getSize();
				location = component.getLocation();
				Dimension shellSize = SwingUtilities.getWindowAncestor(component).getSize();
				size.height = shellSize.height-22;
				location.y = 21;
				try {
					resize(location, size, Cursor.MOVE_CURSOR, 0, 0);						
				} finally {
					location = null;
					size = null;
					component.setCursor(MoveResize.getCursor(Cursor.DEFAULT_CURSOR));
				}
			} else if ( e.getKeyChar() == '2'
						|| e.getKeyChar() == '3'
						|| e.getKeyChar() == '4'
						|| e.getKeyChar() == '5'
				) {
				e.consume();
				int factor = e.getKeyChar() - '0';
				Dimension shellSize = SwingUtilities.getWindowAncestor(component).getSize();
				size = new Dimension(shellSize.width/factor, shellSize.height/factor);
				location = component.getLocation();
				try {
					resize(location, size,  Cursor.MOVE_CURSOR, 0, 0);						
				} finally {
					location = null;
					size = null;
					component.setCursor(MoveResize.getCursor(Cursor.DEFAULT_CURSOR));
				}
			} else if (e.getKeyChar() == '=') {
				e.consume();
				size = component.getSize();
				Dimension shellSize = SwingUtilities.getWindowAncestor(component).getSize();
				location = new Point((shellSize.width - size.width)/2, (shellSize.height - size.height)/2);
				try {
					resize(location, size, Cursor.MOVE_CURSOR, 0, 0);						
				} finally {
					location = null;
					size = null;
					component.setCursor(MoveResize.getCursor(Cursor.DEFAULT_CURSOR));
				}
			} else if (e.getKeyCode() == KeyEvent.VK_UP 
					|| e.getKeyCode() == KeyEvent.VK_DOWN
					|| e.getKeyCode() == KeyEvent.VK_LEFT
					|| e.getKeyCode() == KeyEvent.VK_RIGHT) {

				e.consume();
				
				Rectangle shellBounds = SwingUtilities.getWindowAncestor(component).getBounds();
				if (e.isMetaDown() && (!e.isControlDown() && !e.isAltDown())) {
					switch(e.getKeyCode()) { 
					case KeyEvent.VK_UP:
						size = component.getSize();
						location = component.getLocation();
						location.y = 21;
						if (e.isShiftDown()) {
							location.x = shellBounds.x;
							size.width = shellBounds.width;
						}
						break;
					case KeyEvent.VK_DOWN:
						size = component.getSize();
						location = component.getLocation();
						location.y = shellBounds.height - size.height;
						if (e.isShiftDown()) {
							location.x = shellBounds.x;
							size.width = shellBounds.width;
						}
						break;
					case KeyEvent.VK_LEFT:
						size = component.getSize();
						location = component.getLocation();
						location.x = 0;
						if (e.isShiftDown()) {
							location.y = 21;
							size.height = shellBounds.height-21;
						}
						break;
					case KeyEvent.VK_RIGHT:
						size = component.getSize();
						location = component.getLocation();
						location.x = shellBounds.width - size.width;
						if (e.isShiftDown()) {
							location.y = 21;
							size.height = shellBounds.height-21;
						}
						break;
					}
					try {
						resize(location, size, Cursor.MOVE_CURSOR, 0, 0);						
					} finally {
						location = null;
						size = null;
						component.setCursor(MoveResize.getCursor(Cursor.DEFAULT_CURSOR));
					}
					return;
				}
				if (e.isMetaDown()) {
					return;
				}
				location = component.getLocation();
				size = component.getSize();

				int delta = 0;
				if (!e.isShiftDown() && !e.isControlDown()) {
					delta = 10;
				} else if (e.isShiftDown() && !e.isControlDown()) {
					delta = 100;
				} else if (!e.isShiftDown() && e.isControlDown()) {
					delta = 1;
				}
				boolean moving = true;
				if ((e.getModifiers() & InputEvent.ALT_MASK) != 0) {
					moving = false;
				}
				try {
					switch(e.getKeyCode()) { 
					case KeyEvent.VK_UP:
						resize(location, size, (moving ? Cursor.MOVE_CURSOR : (grow ? Cursor.N_RESIZE_CURSOR : Cursor.S_RESIZE_CURSOR)), 0, -delta);														
						break;
					case KeyEvent.VK_DOWN:
						resize(location, size, (moving ? Cursor.MOVE_CURSOR : (grow ? Cursor.S_RESIZE_CURSOR : Cursor.N_RESIZE_CURSOR)), 0, delta);						
						break;
					case KeyEvent.VK_LEFT:
						resize(location, size, (moving ? Cursor.MOVE_CURSOR : (grow ? Cursor.W_RESIZE_CURSOR : Cursor.E_RESIZE_CURSOR)), -delta, 0);						
						break;
					case KeyEvent.VK_RIGHT:
						resize(location, size, (moving ? Cursor.MOVE_CURSOR : (grow ? Cursor.E_RESIZE_CURSOR : Cursor.W_RESIZE_CURSOR)), delta, 0);						
						break;
					}
				} finally {
					location = null;
					size = null;
				}
			}
		}
	};

	public void attach() {
		super.attach();
		component.addKeyListener(keyListener);
	}

	public void dettach() {
		super.dettach();
		component.removeKeyListener(keyListener);
	}

	protected void process(int deltaX, int deltaY, STATE state, int x, int y) {
		JComponent component = getComponent();
		
		int operation = Integer.MIN_VALUE;
		Point location = null;
		Dimension size = null;
		switch (state) {
		case BEGIN:
			component.putClientProperty(RESIZING, Boolean.TRUE);
			
			operation = operation(component, x, y);
			location = component.getLocation();
			size = component.getSize();
			
			component.putClientProperty(OPERATION, operation);
			component.putClientProperty(LOCATION, location);
			component.putClientProperty(SIZE, size);
			
			resize(location, size, operation, deltaX, deltaY);
			break;
		case IN_PROGRESS:
			component.putClientProperty(RESIZING, Boolean.TRUE);
			
			operation = (Integer) component.getClientProperty(OPERATION);
			location = (Point) component.getClientProperty(LOCATION);
			size = (Dimension) component.getClientProperty(SIZE);
			
			resize(location, size, operation, deltaX, deltaY);
			break;
		case END:
			component.putClientProperty(RESIZING, null);
			
			operation = (Integer) component.getClientProperty(OPERATION);
			location = (Point) component.getClientProperty(LOCATION);
			size = (Dimension) component.getClientProperty(SIZE);
			
			resize(location, size, operation, deltaX, deltaY);
			
			component.putClientProperty(OPERATION, null);
			component.putClientProperty(LOCATION, null);
			component.putClientProperty(SIZE, null);

			break;
		}
	}
	
	protected void setCursor(Point xy) {
		int oper = operation(component, xy.x, xy.y);
		component.setCursor(MoveResize.getCursor(oper));
	}
	
	void resize(Point location, Dimension size, int operation, int deltaX, int deltaY) {
		Rectangle bounds = new Rectangle(location.x, location.y, size.width, size.height);
		
		switch (operation) {
		case Cursor.SW_RESIZE_CURSOR:
			bounds.x += deltaX;
			bounds.width -= deltaX;
			bounds.height += deltaY;
			break;
		case Cursor.W_RESIZE_CURSOR:
			bounds.x += deltaX;
			bounds.width -= deltaX;
			break;
		case Cursor.NW_RESIZE_CURSOR:
			bounds.x += deltaX;
			bounds.y += deltaY;
			bounds.width -= deltaX;
			bounds.height -= deltaY;
			break;
		case Cursor.N_RESIZE_CURSOR:
			bounds.y += deltaY;
			bounds.height -= deltaY;
			break;		
		case Cursor.NE_RESIZE_CURSOR:
			bounds.y += deltaY;
			bounds.width += deltaX;
			bounds.height -= deltaY;
			break;
		case Cursor.SE_RESIZE_CURSOR:
			bounds.width += deltaX;
			bounds.height += deltaY;
			break;
		case Cursor.S_RESIZE_CURSOR:
			bounds.height += deltaY;
			break;
		case Cursor.E_RESIZE_CURSOR:
			bounds.width += deltaX;
			break;
		case Cursor.MOVE_CURSOR:
			bounds.x += deltaX;
			bounds.y += deltaY;
			break;
		}
		
		bounds.width = Math.max(2*BORDER_THICKNESS, bounds.width);
		bounds.height = Math.max(2*BORDER_THICKNESS, bounds.height);
		
		component.setBounds(bounds);
		component.setCursor(MoveResize.getCursor(operation));
		component.repaint();
		SwingUtilities.getWindowAncestor(component).repaint();
	}

	private static int operation(JComponent component, int x, int y) {
		Dimension size = component.getSize();
		if (x >= 0 && x < BORDER_THICKNESS) {
			if (y >= 0 && y < BORDER_THICKNESS) {
				return Cursor.NW_RESIZE_CURSOR;				
			} else if (y >= BORDER_THICKNESS && y < (size.height - BORDER_THICKNESS)) {
				return Cursor.W_RESIZE_CURSOR;
			} else if (y >= (size.height - BORDER_THICKNESS) && y <= size.height) {
				return Cursor.SW_RESIZE_CURSOR;
			}
		} else if (x >= BORDER_THICKNESS && x < (size.width - BORDER_THICKNESS)) {
			if (y >= 0 && y < BORDER_THICKNESS) {
				return Cursor.N_RESIZE_CURSOR;				
			} else if (y >= BORDER_THICKNESS && y < (size.height - BORDER_THICKNESS)) {
				return Cursor.MOVE_CURSOR;
			} else if (y >= (size.height - BORDER_THICKNESS) && y <= size.height) {
				return Cursor.S_RESIZE_CURSOR;
			}
		} else if (x >= (size.width - BORDER_THICKNESS) && x < size.width) {
			if (y >= 0 && y < BORDER_THICKNESS) {
				return Cursor.NE_RESIZE_CURSOR;				
			} else if (y >= BORDER_THICKNESS && y < (size.height - BORDER_THICKNESS)) {
				return Cursor.E_RESIZE_CURSOR;
			} else if (y >= (size.height - BORDER_THICKNESS) && y <= size.height) {
				return Cursor.SE_RESIZE_CURSOR;
			}
		}
		return Cursor.DEFAULT_CURSOR;
	}
	
	static boolean isResizing(JComponent component) {
		return component.getClientProperty(RESIZING) != null;
	}
	
	static boolean isShrink(JComponent component) {
		return component.getClientProperty(SHRINK) != null;
	}
}
